package cc.shacocloud.mirage.restful.bind.validation.errors;

import cc.shacocloud.mirage.utils.Utils;
import cc.shacocloud.mirage.utils.charSequence.StrUtil;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ElementKind;
import jakarta.validation.Path;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Objects;

/**
 * 基于 {@link ConstraintViolation} 的 {@link BindingResultError}
 *
 * @author 思追(shaco)
 */
public class ConstraintViolationError implements BindingResultError {
    
    @NotNull
    private final ConstraintViolation<Object> constraintViolation;
    
    private String nestedPath;
    
    private String code;
    
    public ConstraintViolationError(@NotNull ConstraintViolation<Object> constraintViolation) {
        this.constraintViolation = constraintViolation;
    }
    
    /**
     * 解析约束冲突的错误代码
     */
    private static @NotNull String parseCode(@NotNull ConstraintViolation<?> constraintViolation) {
        String messageTemplate = constraintViolation.getMessageTemplate();
        if (StrUtil.isNotEmpty(messageTemplate)) {
            return StrUtil.trimTrailingCharacter(
                    StrUtil.trimLeadingCharacter(messageTemplate, '{')
                    , '}');
        } else {
            return constraintViolation.getMessage();
        }
    }
    
    /**
     * 为给定的约束冲突确定一个字段。
     * <p>
     * 实现返回字符串化的属性路径。
     */
    private static @NotNull String determineField(@NotNull ConstraintViolation<?> violation) {
        Path path = violation.getPropertyPath();
        StringBuilder sb = new StringBuilder();
        boolean first = true;
        for (Path.Node node : path) {
            if (node.isInIterable()) {
                sb.append('[');
                Object index = node.getIndex();
                if (index == null) {
                    index = node.getKey();
                }
                if (index != null) {
                    sb.append(index);
                }
                sb.append(']');
            }
            String name = node.getName();
            ElementKind elementKind = node.getKind();
            if (name != null
                    // 属性 和 参数
                    && (elementKind.equals(ElementKind.PROPERTY) || elementKind.equals(ElementKind.PARAMETER))
                    && !name.startsWith("<")) {
                if (!first) {
                    sb.append('.');
                }
                first = false;
                sb.append(name);
            }
        }
        return sb.toString();
    }
    
    @Override
    public @NotNull String getCode() {
        if (Objects.isNull(code)) {
            this.code = parseCode(constraintViolation);
        }
        return this.code;
    }
    
    @Override
    public @NotNull String getNestedPath() {
        if (Objects.isNull(nestedPath)) {
            this.nestedPath = determineField(constraintViolation);
        }
        return this.nestedPath;
    }
    
    @Override
    public @Nullable Object getRejectedValue() {
        return constraintViolation.getInvalidValue();
    }
    
    @Override
    public String toString() {
        return "字段[" + getNestedPath() + "]错误，错误代码：" + getCode() + "，错误值" + Utils.nullSafeToString(getRejectedValue());
    }
    
}
